package com.copyctrl.campus.portal.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.copyctrl.campus.common.constant.RedisConstant;
import com.copyctrl.campus.common.constant.RedisKind;
import com.copyctrl.campus.common.domain.RedisData;
import com.copyctrl.campus.common.domain.ScrollResult;
import com.copyctrl.campus.common.domain.SendData;
import com.copyctrl.campus.portal.dto.PassageDTO;
import com.copyctrl.campus.portal.service.PassageService;
import com.copyctrl.campus.portal.util.RedisUtil;
import com.copyctrl.mapper.CampKindMapper;
import com.copyctrl.mapper.CampPassageMapper;
import com.copyctrl.model.*;
import com.copyctrl.publisher.send.DataPublisher;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 消息队列做异步存储
 *
 * @author:JH
 * @version:1.0
 */
@Service
public class PassageServiceImpl implements PassageService {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    CampKindMapper kindMapper;
    @Autowired
    CampPassageMapper passageMapper;

    @Autowired
    DataPublisher dataPublisher;

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    /**
     * @return 先从redis中取，如果redis中没有查到，再从数据库中查询并把文章类型数据存入redis中(交给消息队列)
     */
    @Override
    public List<CampKind> getAllKind() {
        CampKindExample find = new CampKindExample();
        CampKindExample.Criteria criteria = find.createCriteria();
        criteria.andIdIsNotNull();
        List<CampKind> campKinds = kindMapper.selectByExample(find);
        return campKinds;
    }

    /**
     * @return 不能是简单的查询所有文章，要分页查询，每次下划查询下一页，先从redis中查，redis中没有再从mysql中查
     * redis中使用sorted set存储文章信息，同时默认使用时间作为score。必须使用滚动翻页
     * mysql中也没有就返回一个标志，前端进行阻断，不再查询
     */
    @Override
    public ScrollResult<PassageDTO> getAllPassages(Long max, int offset) {
        ScrollResult scrollResult = RedisUtil.getDataByPage(stringRedisTemplate, RedisConstant.PASSAGE_ALL, max, offset, 10, PassageDTO.class);
        System.out.println(scrollResult);
        if (scrollResult.getList() == null || scrollResult.getList().size() == 0) {
            //如果从redis中没有查询到数据，就从mysql中查，查询条件是小于传入的时间的数据
            PageHelper.offsetPage(0, 10);
            CampPassageExample allPassages = new CampPassageExample();
            CampPassageExample.Criteria criteria = allPassages.createCriteria().andCreateTimeLessThan(dateFormat.format(new Date(max - 1)));
            allPassages.setOrderByClause("create_time");
            criteria.andIdIsNotNull();
            List<CampPassage> campPassages = passageMapper.selectByExample(allPassages);
            System.out.println(campPassages);
            List<PassageDTO> passageDTOS = BeanUtil.copyToList(campPassages, PassageDTO.class);
            scrollResult.setList(passageDTOS);
            //如果mysql中可以查到数据，就同步到redis 使用消息队列
            if (passageDTOS.size() > 1) {
                Map dataMap = new HashMap();
                for (PassageDTO passageDTO : passageDTOS) {
                    dataMap.put(passageDTO.getCreateTime().getTime() + passageDTO.getId(), JSONUtil.toJsonStr(passageDTO));
                }
                RedisData data = RedisData.getRedisDataMap(RedisKind.SORTEDSET, dataMap, "add", RedisConstant.PASSAGE_ALL);
                data.setUseFor(true);
                SendData send = new SendData();
                send.setRedisData(data);
                send.setDataType(true);
                dataPublisher.redisData(JSONUtil.toJsonStr(send));
            } else {
                //防止缓存穿透
            }
        }
        return scrollResult;
    }

    @Override
    public ScrollResult<PassageDTO> getByKindId(Integer kind, int page, Long max) {
        ScrollResult scrollResult = RedisUtil.getDataByPage(stringRedisTemplate,
                RedisConstant.PASSAGE_KIND + kind,
                max, page, 10, PassageDTO.class);
        if (scrollResult.getList() == null || scrollResult.getList().size() == 0) {
            //如果从redis中没有查询到数据，就从mysql中查
            PageHelper.startPage(0, 10);
            CampPassageExample some = new CampPassageExample();
            CampPassageExample.Criteria criteria = some.createCriteria().andCreateTimeLessThan(dateFormat.format(new Date(max - 1)));
            criteria.andKindEqualTo(kind);
            some.setOrderByClause("create_time");
            List<CampPassage> campPassages = passageMapper.selectByExample(some);
            //如果mysql中可以查到数据，就同步到redis  使用消息队列
            if (campPassages.size() > 0) {
                List<PassageDTO> passageDTOS = BeanUtil.copyToList(campPassages, PassageDTO.class);
                Map dataMap = new HashMap();
                for (PassageDTO passageDTO : passageDTOS) {
                    dataMap.put(passageDTO.getCreateTime().getTime() + passageDTO.getId(), JSONUtil.toJsonStr(passageDTO));
                }
                RedisData data = RedisData.getRedisDataMap(RedisKind.SORTEDSET, dataMap, "add", RedisConstant.PASSAGE_KIND + kind);
                data.setUseFor(true);
                SendData send = new SendData();
                send.setRedisData(data);
                send.setDataType(true);
                dataPublisher.redisData(JSONUtil.toJsonStr(send));
            }
        }
        return scrollResult;
    }

    @Override
    public CampPassage findById(Integer id) {
        String json = stringRedisTemplate.opsForValue().get(RedisConstant.PASSAGE_KEY + id);
        CampPassage passage = JSONUtil.toBean(json, CampPassage.class);
        if (passage.getId() == null) {
            passage = passageMapper.selectByPrimaryKey(id);
            // 使用消息队列异步
            RedisData data = RedisData.getRedisData(String.class, RedisKind.STRING, JSONUtil.toJsonStr(passage), "set", RedisConstant.PASSAGE_KEY + id);
            SendData send = new SendData();
            send.setDataType(true);
            send.setRedisData(data);
            dataPublisher.redisData(JSONUtil.toJsonStr(send));
        }
        execute(passage);
        return passage;
    }

    @Override
    public List<PassageDTO> findHot() {
        List<PassageDTO> range = stringRedisTemplate.opsForList().range(RedisConstant.PASSAGE_TOP, 0, -1).stream().map(json -> {
            return JSONUtil.toBean(json, PassageDTO.class);
        }).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(range)) {
            PageHelper.startPage(1, 10);
            CampPassageExample example = new CampPassageExample();
            example.setOrderByClause("hot");
            range = passageMapper.selectByExample(example).stream().map(
                    passage -> {
                        return BeanUtil.copyProperties(passage, PassageDTO.class);
                    }
            ).collect(Collectors.toList());
            List<String> collect = range.stream().map(passageDTO -> {
                return JSONUtil.toJsonStr(passageDTO);
            }).collect(Collectors.toList());
            stringRedisTemplate.opsForList().leftPushAll(RedisConstant.PASSAGE_TOP, collect);
            stringRedisTemplate.expire(RedisConstant.PASSAGE_TOP, RedisConstant.SH, TimeUnit.MILLISECONDS);
        }
        return range;
    }

    @Override
    public List<PassageDTO> findLike(List<Integer> list) {
        if (list.size() == 0) {
            for (int i = 0; i < 5; i++) {
                list.add(i);
            }
        }
        CampPassageExample example = new CampPassageExample();
        example.createCriteria().andKindIn(list);
        example.setOrderByClause("hot");
        PageHelper.startPage(0, 10);
        List<PassageDTO> collect = passageMapper.selectByExample(example)
                .stream()
                .map(campPassage -> BeanUtil.copyProperties(campPassage, PassageDTO.class))
                .collect(Collectors.toList());
        return collect;
    }

    @Async("defaultThreadPoolExecutor")
    public void execute(CampPassage passage) {
        System.out.println(passage);
        passage.setHot(passage.getHot() + 1);
        CampPassageExample example = new CampPassageExample();
        example.createCriteria().andIdEqualTo(passage.getId());
        passageMapper.updateByExampleSelective(passage, example);
        stringRedisTemplate.opsForValue().set(RedisConstant.PASSAGE_KEY + passage.getId(), JSONUtil.toJsonStr(passage));
    }
}
